package com.ks.api.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ks.api.dto.KuaishouTokenDTO;
import com.ks.api.entity.KsConfigSc;
import com.ks.api.mapper.KsConfigScMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 快手API Token服务
 */
@Slf4j
@Service
public class KuaishouTokenService {

    @Autowired
    private KsConfigScMapper KsConfigScMapper;

    // Token API URLs
    private static final String GET_TOKEN_URL = "https://ad.e.kuaishou.com/rest/openapi/oauth2/authorize/access_token";
    private static final String REFRESH_TOKEN_URL = "https://ad.e.kuaishou.com/rest/openapi/oauth2/authorize/refresh_token";
    
    // 本地保存的token文件路径前缀
    private static final String TOKEN_FILE_PREFIX = "kuaishou_token_";
    private static final String TOKEN_FILE_SUFFIX = ".json";
    
    // 内存中缓存的token信息，按应用ID缓存
    private final Map<String, KuaishouTokenDTO> tokenCacheMap = new HashMap<>();
    
    // token初始化状态
    private final AtomicBoolean tokenInitialized = new AtomicBoolean(false);
    
    // token监控线程池
    private ScheduledExecutorService tokenMonitorExecutor;
    
    // 提前多少毫秒刷新token (30分钟)
    private static final long REFRESH_BEFORE_EXPIRE = 30 * 60 * 1000;
    
    // 授权码Future，按应用ID缓存
    private final Map<String, CompletableFuture<String>> authCodeFutureMap = new HashMap<>();
    private static final long AUTH_CODE_WAIT_TIMEOUT = 300; // 等待auth code的超时时间（秒）

    private final ExecutorService tokenInitExecutor = Executors.newSingleThreadExecutor();

    private Map<String,String> map = new HashMap<>();

    /**
     * 应用启动时异步初始化token
     */
    @PostConstruct
    public void init() {
        log.info("开始异步初始化快手API Token...");
        tokenInitExecutor.submit(this::initializeToken);
    }
    
    /**
     * 异步初始化token的具体实现
     */
    private void initializeToken() {
        try {
            log.info("初始化快手API Token...");
            
            // 从数据库获取所有应用配置
            initAuthCodeFutures();
            
            // 检查本地token文件
            for (String appId : authCodeFutureMap.keySet()) {
                if (hasLocalToken(appId)) {
                    // 读取本地token并刷新
                    refreshTokenFromLocal(appId);
                } else {
                    // 首次获取token，等待回调获取auth_code
                    log.info("等待获取app_id={}的auth_code...", appId);
                    try {
                        KsConfigSc KsConfigSc = KsConfigScMapper.getKsConfigScByAppId(appId);
                        if (KsConfigSc != null && !StrUtil.isBlank(KsConfigSc.getAuthCode())) {
                            // 如果数据库已有auth_code，直接使用
                            getTokenByAuthCode(appId);
                        } else {
                            // 否则等待回调
                            CompletableFuture<String> authCodeFuture = authCodeFutureMap.get(appId);
                            String receivedAuthCode = authCodeFuture.get(AUTH_CODE_WAIT_TIMEOUT, TimeUnit.SECONDS);
                            log.info("成功获取app_id={}的auth_code: {}", appId, receivedAuthCode);
                            
                            // 保存auth_code到数据库
                            KsConfigSc config = new KsConfigSc();
                            config.setAppDh(appId);
                            config.setAuthCode(receivedAuthCode);
                            //临时存储一下code
                            map.put(appId,receivedAuthCode);
                            //KsConfigScMapper.updateAuthCode(appId, receivedAuthCode);
                            
                            // 获取token
                            getTokenByAuthCode(appId);
                        }
                    } catch (TimeoutException e) {
                        log.error("等待app_id={}的auth_code超时", appId, e);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        log.error("等待app_id={}的auth_code被中断", appId, e);
                    } catch (ExecutionException e) {
                        log.error("获取app_id={}的auth_code时发生错误", appId, e);
                    }
                }
            }

            // 标记token初始化完成
            tokenInitialized.set(true);
            
            // 启动token监控线程
            startTokenMonitor();
            
        } catch (Exception e) {
            log.error("初始化快手API Token失败", e);
        }
    }
    
    /**
     * 初始化授权码Future
     */
    private void initAuthCodeFutures() {
        try {
            // 获取所有应用配置
            for (KsConfigSc KsConfigSc : KsConfigScMapper.getAllKsConfigScs()) {
                String appBh = KsConfigSc.getAppDh();
                if (!authCodeFutureMap.containsKey(appBh)) {
                    authCodeFutureMap.put(appBh, new CompletableFuture<>());
                }
            }
        } catch (Exception e) {
            log.error("初始化授权码Future失败", e);
        }
    }
    
    /**
     * 应用关闭时清理资源
     */
    @PreDestroy
    public void destroy() {
        if (tokenMonitorExecutor != null && !tokenMonitorExecutor.isShutdown()) {
            tokenMonitorExecutor.shutdown();
            log.info("关闭Token监控线程池");
        }
        if (tokenInitExecutor != null) {
            tokenInitExecutor.shutdownNow();
        }
    }
    
    /**
     * 启动token监控线程
     * 每10秒检查一次token状态，如果即将过期则刷新
     */
    private void startTokenMonitor() {
        tokenMonitorExecutor = new ScheduledThreadPoolExecutor(1, r -> {
            Thread thread = new Thread(r, "token-monitor");
            thread.setDaemon(true);
            return thread;
        });
        
        tokenMonitorExecutor.scheduleAtFixedRate(() -> {
            try {
                for (String appId : tokenCacheMap.keySet()) {
                    checkAndRefreshToken(appId);
                }
            } catch (Exception e) {
                log.error("Token监控线程执行异常", e);
            }
        }, 10, 10, TimeUnit.SECONDS);
        
        log.info("Token监控线程已启动，每10秒检查一次token状态");
    }
    
    /**
     * 检查token是否即将过期，如果是则刷新
     */
    private void checkAndRefreshToken(String appId) {
        KuaishouTokenDTO tokenCache = tokenCacheMap.get(appId);
        
        if (tokenCache == null) {
            log.warn("App ID={}的Token缓存为空，尝试重新获取", appId);
            getTokenByAuthCode(appId);
            return;
        }
        
        long currentTime = System.currentTimeMillis();
        long tokenUpdateTime = tokenCache.getUpdateTime();
        long accessTokenExpireTime = tokenUpdateTime + (tokenCache.getAccessTokenExpiresIn() * 1000);
        
        // 如果token将在30分钟内过期，则刷新
        if (currentTime > accessTokenExpireTime - REFRESH_BEFORE_EXPIRE) {
            log.info("App ID={}的Token即将在{}分钟后过期，开始刷新Token", 
                    appId, (accessTokenExpireTime - currentTime) / 60000);
            refreshToken(appId);
        }
    }

    /**
     * 使用授权码获取access_token
     */
    public KuaishouTokenDTO getTokenByAuthCode(String appId) {
        log.info("通过授权码获取app_id={}的token...", appId);
        
        KsConfigSc KsConfigSc = KsConfigScMapper.getKsConfigScByAppId(appId);
        if (KsConfigSc == null) {
            log.error("未找到App ID={}的配置信息", appId);
            return null;
        }
        
        JSONObject params = new JSONObject();
        params.set("app_id", KsConfigSc.getZtId());
        params.set("secret", KsConfigSc.getZtSc());
        params.set("auth_code", map.get(appId));
        
        try {
            HttpResponse response = HttpRequest.post(GET_TOKEN_URL)
                    .header("Content-Type", "application/json")
                    .body(params.toString())
                    .execute();
            
            String respBody = response.body();
            log.info("获取app_id={}的token响应: {}", appId, respBody);
            
            JSONObject result = JSONUtil.parseObj(respBody);
            if (result.getInt("code") == 0) {
                JSONObject data = result.getJSONObject("data");
                
                KuaishouTokenDTO tokenDTO = new KuaishouTokenDTO();
                tokenDTO.setAccessToken(data.getStr("access_token"));
                tokenDTO.setRefreshToken(data.getStr("refresh_token"));
                tokenDTO.setAccessTokenExpiresIn(data.getLong("access_token_expires_in"));
                tokenDTO.setRefreshTokenExpiresIn(data.getLong("refresh_token_expires_in"));
                tokenDTO.setUpdateTime(System.currentTimeMillis());
                
                // 保存到本地和缓存
                saveTokenToLocal(appId, tokenDTO);
                tokenCacheMap.put(appId, tokenDTO);

                // 标记token初始化完成
                tokenInitialized.set(true);

                return tokenDTO;
            } else {
                log.error("获取app_id={}的token失败: code={}, message={}", 
                        appId, result.getInt("code"), result.getStr("message"));
                return null;
            }
        } catch (Exception e) {
            log.error("获取app_id={}的token异常", appId, e);
            return null;
        }
    }

    /**
     * 刷新token
     */
    public synchronized KuaishouTokenDTO refreshToken(String appId) {
        log.info("刷新app_id={}的token...", appId);
        
        KuaishouTokenDTO tokenCache = tokenCacheMap.get(appId);
        if (tokenCache == null) {
            log.warn("刷新app_id={}的token失败：本地没有token信息", appId);
            return getTokenByAuthCode(appId);
        }
        
        KsConfigSc KsConfigSc = KsConfigScMapper.getKsConfigScByAppId(appId);
        if (KsConfigSc == null) {
            log.error("未找到App ID={}的配置信息", appId);
            return null;
        }
        
        JSONObject params = new JSONObject();
        params.set("app_id", KsConfigSc.getZtId());
        params.set("secret", KsConfigSc.getZtSc());
        params.set("refresh_token", tokenCache.getRefreshToken());
        
        try {
            HttpResponse response = HttpRequest.post(REFRESH_TOKEN_URL)
                    .header("Content-Type", "application/json")
                    .body(params.toString())
                    .execute();
            
            String respBody = response.body();
            log.info("刷新app_id={}的token响应: {}", appId, respBody);
            
            JSONObject result = JSONUtil.parseObj(respBody);
            if (result.getInt("code") == 0) {
                JSONObject data = result.getJSONObject("data");
                
                KuaishouTokenDTO tokenDTO = new KuaishouTokenDTO();
                tokenDTO.setAccessToken(data.getStr("access_token"));
                tokenDTO.setRefreshToken(data.getStr("refresh_token"));
                tokenDTO.setAccessTokenExpiresIn(data.getLong("access_token_expires_in"));
                tokenDTO.setRefreshTokenExpiresIn(data.getLong("refresh_token_expires_in"));
                tokenDTO.setUpdateTime(System.currentTimeMillis());
                
                // 保存到本地和缓存
                saveTokenToLocal(appId, tokenDTO);
                tokenCacheMap.put(appId, tokenDTO);
                
                log.info("App ID={}的Token刷新成功，有效期至: {}", 
                        appId, DateUtil.formatDateTime(new Date(System.currentTimeMillis() + tokenDTO.getAccessTokenExpiresIn() * 1000)));
                
                return tokenDTO;
            } else {
                log.error("刷新app_id={}的token失败: code={}, message={}", 
                        appId, result.getInt("code"), result.getStr("message"));
                // 如果刷新失败，尝试重新获取token
                return getTokenByAuthCode(appId);
            }
        } catch (Exception e) {
            log.error("刷新app_id={}的token异常", appId, e);
            return null;
        }
    }

    /**
     * 检查是否有本地token文件
     */
    private boolean hasLocalToken(String appId) {
        Path path = Paths.get(getTokenFilePath(appId));
        return Files.exists(path);
    }

    /**
     * 获取token文件路径
     */
    private String getTokenFilePath(String appId) {
        return TOKEN_FILE_PREFIX + appId + TOKEN_FILE_SUFFIX;
    }

    /**
     * 从本地读取token并刷新
     */
    private void refreshTokenFromLocal(String appId) {
        try {
            Path path = Paths.get(getTokenFilePath(appId));
            String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
            
            if (StrUtil.isNotBlank(content)) {
                KuaishouTokenDTO localToken = JSONUtil.toBean(content, KuaishouTokenDTO.class);
                tokenCacheMap.put(appId, localToken);
                
                // 计算token是否还有效
                long currentTime = System.currentTimeMillis();
                long tokenUpdateTime = localToken.getUpdateTime();
                long accessTokenExpireTime = tokenUpdateTime + (localToken.getAccessTokenExpiresIn() * 1000);
                
                if (currentTime > accessTokenExpireTime - REFRESH_BEFORE_EXPIRE) { // 如果过期或即将过期，刷新token
                    log.info("App ID={}的本地token已过期或即将过期，刷新token", appId);
                    refreshToken(appId);
                } else {
                    log.info("App ID={}的本地token有效，有效期至: {}", 
                            appId, DateUtil.formatDateTime(new Date(accessTokenExpireTime)));
                }
            } else {
                log.warn("App ID={}的本地token文件内容为空，重新获取token", appId);
                getTokenByAuthCode(appId);
            }
        } catch (Exception e) {
            log.error("从本地刷新app_id={}的token失败", appId, e);
            getTokenByAuthCode(appId);
        }
    }

    /**
     * 保存token到本地文件
     */
    private void saveTokenToLocal(String appId, KuaishouTokenDTO tokenDTO) {
        try {
            Path path = Paths.get(getTokenFilePath(appId));
            String content = JSONUtil.toJsonStr(tokenDTO);
            Files.write(path, content.getBytes(StandardCharsets.UTF_8));
            log.info("App ID={}的Token已保存到本地文件: {}", appId, getTokenFilePath(appId));
        } catch (IOException e) {
            log.error("保存app_id={}的token到本地文件失败", appId, e);
        }
    }

    /**
     * 获取指定应用的访问token
     */
    public String getAccessToken(String appId) {
        KuaishouTokenDTO tokenCache = tokenCacheMap.get(appId);
        
        if (tokenCache == null) {
            refreshTokenFromLocal(appId);
            tokenCache = tokenCacheMap.get(appId);
            
            if (tokenCache == null) {
                getTokenByAuthCode(appId);
                tokenCache = tokenCacheMap.get(appId);
            }
        }
        
        return tokenCache != null ? tokenCache.getAccessToken() : null;
    }
    
    /**
     * 获取所有应用的访问token
     */
    public Map<String, String> getAllAccessTokens() {
        Map<String, String> result = new HashMap<>();
        
        for (String appId : authCodeFutureMap.keySet()) {
            String token = getAccessToken(appId);
            if (token != null) {
                result.put(appId, token);
            }
        }
        
        return result;
    }
    
    /**
     * 检查token是否已初始化
     */
    public boolean isTokenInitialized() {
        return tokenInitialized.get();
    }

    /**
     * 每天凌晨3点和下午16点执行刷新token
     */
    @Scheduled(cron = "0 0 3,16 * * ?")
    public void scheduledRefreshToken() {
        log.info("定时任务刷新所有token - 时间: {}", DateUtil.now());
        
        for (String appId : authCodeFutureMap.keySet()) {
            refreshToken(appId);
        }
    }

    /**
     * 处理接收到的auth_code
     * @param appDh 应用标号
     * @param newAuthCode 新的auth_code
     */
    public void onAuthCodeReceived(String appDh, String newAuthCode) {
        // 根据应用标号获取appId
        String appId = getAppIdByAppDh(appDh);
        if (appId == null) {
            log.error("未找到app_dh={}对应的应用配置", appDh);
            return;
        }
        
        CompletableFuture<String> authCodeFuture = authCodeFutureMap.get(appId);
        
        if (authCodeFuture != null && !authCodeFuture.isDone()) {
            log.info("收到app_dh={}(appId={})的auth_code: {}", appDh, appId, newAuthCode);
            authCodeFuture.complete(newAuthCode);
            
            // 根据应用标号更新数据库中的auth_code
            //KsConfigScMapper.updateAuthCodeByAppDh(appDh, newAuthCode);
        } else {
            log.warn("应用标号{}(appId={})的auth_code future不存在或已完成", appDh, appId);
        }
    }
    
    /**
     * 根据应用标号获取应用ID
     * @param appDh 应用标号
     * @return 应用ID
     */
    private String getAppIdByAppDh(String appDh) {
        KsConfigSc KsConfigSc = KsConfigScMapper.getKsConfigScByAppDh(appDh);
        return KsConfigSc != null ? KsConfigSc.getAppDh() : null;
    }
} 